/**************************************************************************
 * @doc RS232Com
 * @module Rs232.C - Source code for Rs232 communication handling functions.|  
 * Source code for Rs232 Communication handling functions.
 *
 * Product     :   SoftPay 2000 (Global Payment Methodology).
 * <nl>Developer   :   SoftPay Development Team (I_SP2000).
 * <nl>Notes       :
 *
 * @head3 Rs232 Functions |
 * @index | Rs232Com
 * @end
 *
 * Copyright (c) 1996-2000 by VeriFone Inc. All rights reserved.
 * 
 * No part of this software may be used, stored, compiled, reproduced,
 * modified, transcribed, translated, transmitted, or transferred, in any form 
 * or by any means whether electronic, mechanical, magnetic, optical, 
 * or otherwise, without the express prior written permission of VeriFone, Inc.
 *
 * Revision History
 * ------------------------------------------------------------------------
 $Log: /SoftPay/SPK2SIM/Source/Rs232Com/Rs232.c $
 ********************************************************************************/
#include <project.h>

#include <stdio.h>
#include <ascii.h>
#include <string.h>
#include <svc.h>

#include <aclconio.h>
#include <errno.h>
#include <txoinc.h>
#include <acldev.h>
#include <aclstr.h>
#include <portdef.h>
#include <aclascii.h>
#include <applidl.h>

#include <define.h>
#include <init.h>
#include <ui.h>

#include <Rs232.h>
#include <codesec.h>

unsigned long ulTimerCount;

/****************************************************************************
 * Routine Name:  inOpenComPort() 
 *
 * Description :  Opens the Com port.
 *
 * Input       :  szPortName    -> Port (name) to be opened.
 *                psrOpnBlk     -> Communication parameters block.
 *
 * Output      :  Port Handle   -> On Successful opening of the port
 *                ERR_COM_OPEN  -> If port open failed
 *                ERR_COM_INIT  -> If port setting failed
 *
 * Notes       :  Nil
 ****************************************************************************
 */
int inOpenComPort(const char* szPortName,struct Opn_Blk* psrOpnBlk) {
    int inHandle;

    inHandle = open(szPortName, 0); 
    if (inHandle <= 0)
        return((int) ERR_COM_OPEN);   /* Error Opening the Com Port */

    if (set_opn_blk(inHandle, psrOpnBlk) < 0) {
        close(inHandle);
        return((int) ERR_COM_INIT);   /* Error setting the port's parameters */
    }

    return(inHandle);
}


/****************************************************************************
 * Routine Name:  inCloseComPort() 
 *
 * Description :  Closes the Com port.
 *
 * Input       :  inHandle      -> Port handle  
 *
 * Output      :  VS_SUCCESS    ->  On success
 *                VS_ERR        ->  when invalid Handle
 *                ERR_COM_CLOSE ->  On failure
 *
 * Notes       :  Nil
 ****************************************************************************
 */
int inCloseComPort(int inHandle) {
    if (inHandle < 0)
        return(VS_ERR);
    if (close(inHandle) == 0)
        return(VS_SUCCESS);
    return(ERR_COM_CLOSE);
}



/****************************************************************************
 * Routine name:  inSendComPacket()
 *
 * Description :  This routine sends a formated packet to the com port.
 *
 * Input       :  inHandle      -> Port handle  
 *                pchMsg        -> The data that is to be sent to the Com port.
 *                inMsgSize     -> The size of the data to be sent.
 *                fPadStxEtx    -> Boolean flag to Pad STX/ETX or not.
 *                inACKTimeOut  -> The timeout within which an ACK should be received. (in seconds)
 *                fWaitForACK   -> Boolean flag to indicate whether to wait for an ACK.
 *                inMaxRetries  -> Maximum number of retries allowed.
 *
 * Output      :  VS_SUCCESS    -> if successful
 *                VS_ERR        -> if error.
 *                ERR_COM_SEND  -> if failed to send the packet.
 *                ERR_COM_NAK   -> Too many NAKs.
 *
 * Notes       :    
 ****************************************************************************
 */
int inSendComPacket(int inHandle,char* pchMsg,int inMsgSize,VS_BOOL fPadStxEtx,int inACKTimeOut,VS_BOOL fWaitForAck,int inMaxRetries) {
    char chResp;
    int inSize = 0;
    int inNumRetry = 0;
    char szSendBuf[SEND_BUF_SIZE + 1];


    if (inHandle < 0)
        return(VS_ERR);

    memset(szSendBuf, 0, (int)sizeof(szSendBuf));

    if (fPadStxEtx == VS_TRUE) {
        szSendBuf[inSize] = STX; 
        inSize++;
        memcpy(&(szSendBuf[1]), pchMsg, inMsgSize); 
        inSize += inMsgSize;
        szSendBuf[inSize] = ETX; 
        inSize++;
    } else {
        memcpy(szSendBuf, pchMsg, inMsgSize); 
        inSize = inMsgSize;
    }

    szSendBuf[inSize] = (char) SVC_CRC_CALC(0, &(szSendBuf[1]), (inSize - 1));
    inSize++;

    do {
        /* Clear any prev responses */
        while (read(inHandle, &chResp, 1) > 0)
        ;

        inNumRetry++;
        /* transmit the packet */
        if (write(inHandle, (char *) szSendBuf, inSize) != inSize)
            return(ERR_COM_SEND); 

        ulTimerCount = set_itimeout(NO_CLOCK_HANDLE inACKTimeOut, TM_SECONDS);

        if (fWaitForAck == VS_TRUE) {
            chResp = NAK;              /* Wait for reply till Timeout */
            while ((read(inHandle, &chResp, 1) == 0) && (CHECK_TIMEOUT(ulTimerCount)));
        } else
            chResp = ACK;
    } while ((chResp != ACK) && (inNumRetry < inMaxRetries));

    if (chResp == ACK)
        return (VS_SUCCESS);
    else
        return (ERR_COM_NAK);  /* Too many NAKs, so indicate to the app */
}


/****************************************************************************
 * Routine name:  inRecvPModePacket()
 *
 * Description :  This routine receives a packet from the com port in character Mode.
 *
 * Input       :  inHandle      -> Port handle  
 *                pchMsg        -> The data that is to be sent to the Com port.
 *                inMsgSize     -> The size of the data to be sent.
 *                fPadStxEtx    -> Boolean flag to Pad STX/ETX or not.
 *                inACKTimeOut  -> The timeout within which an ACK should be received.
 *                fWaitForACK   -> Boolean flag to indicate whether to wait for an ACK.
 *                inMaxRetries  -> Maximum number of retries allowed.
 *
 * Output      :  VS_SUCCESS    -> if successful
 *                VS_ERR        -> if error.
 *                ERR_COM_SEND  -> if failed to send the packet.
 *                ERR_COM_NAK   -> Too many NAKs.
 *
 * Notes       :    
 ****************************************************************************
 */
int inRecvPModePacket(int inHandle,char* pchMsg,int inMaxMsgSize,VS_BOOL fValidate,int inRespTimeOut,int inMaxRetries) {
    unsigned char ucTemp;
    int inBytesRead = 0,
    inNAKCount = 0;

    while (1) {
        if (inRespTimeOut)    /* If a timeout was passed */
            ulTimerCount = set_itimeout(NO_CLOCK_HANDLE inRespTimeOut, TM_SECONDS);

        do {
            inBytesRead = read(inHandle, (char *) pchMsg, inMaxMsgSize);
        } while ((inBytesRead == 0) && CHECK_TIMEOUT(ulTimerCount));

        if (inBytesRead < 0)
            return(ERR_COM_READ);

        if (inBytesRead == 0)
            return(ERR_COM_TIMEOUT);  /* Timeout occurred */

        if (fValidate == VS_TRUE) {
            if (inValidatePacket(pchMsg, inBytesRead) != VS_SUCCESS) {
                ucTemp = NAK;
                if (write(inHandle, (char *) &ucTemp, 1) != 1)
                    return(ERR_COM_WRITE);
                if (++inNAKCount > inMaxRetries)
                    return(ERR_COM_RECV);
                continue;
            }
        }

        ucTemp = ACK;
        if (write(inHandle, (char *) &ucTemp, 1) != 1)
            return(ERR_COM_WRITE);

        /* return the packet in pucDst, and number of bytes */
        /* in the packet stripping off the STX, ETX, LRC    */

        memcpy((char *) pchMsg, (char *) pchMsg + 1, (inBytesRead - 3));

        pchMsg[inBytesRead - 3] = 0;
        return(inBytesRead - 3);
    }
}

/****************************************************************************
 * Routine Name:  inValidatePacket()
 * Description :  Validates a packet of the form <STX><Data><ETX><LRC>. 
 * Input       :  Packet      - pucPacket
 *                Packet size - inSize
 * Output      :  VS_SUCCESS     on success ( correct packet format )
 *                VS_ERR         on error.
 * Notes       :  Uses a local buffer for all calculations. Because of this 
 *                all outer layer callers can use the source and destination 
 *                as the same buffer. The packet formed will be null 
 *                terminated also.
 ****************************************************************************
 */
int inValidatePacket(char* pucPkt,int inSize) {
    if (inSize < 4)
        return(VS_ERR);   /* Atleast a pcaket should have <STX><1 char><ETX><LRC> */
    if (pucPkt[0] != STX)
        return(VS_ERR);
    if (pucPkt[inSize - 2] != ETX)
        return(VS_ERR);
    if (ucGetLRC(&(pucPkt[1]), (inSize - 2)) != pucPkt[inSize - 1])
        return(VS_ERR);
    return(VS_SUCCESS);
}

/****************************************************************************
 * Routine Name:  ucGetLRC()
 * Description :  Computes the LRC of the packet of given size.
 * Input       :  Request packet    -  pucPacket
 *                Packet size       -  inSize
 * Output      :  Computed LRC byte.
 * Notes       :  LRC is the XOR of all the bytes in the packet.
 ****************************************************************************
 */
unsigned char ucGetLRC(char* pucPacket,int inSize) {
    unsigned char ucLRC = *pucPacket++;  

    while (--inSize > 0)
        ucLRC ^= *pucPacket++;
    return(ucLRC);
}



/****************************************************************************
 * Routine name:  inRecvCModePacket()
 *
 * Description :  This routine receives a packet from the com port in character Mode.
 *
 * Input       :  inHandle      -> Port handle  
 *                pchMsg        -> The data that is to be sent to the Com port.
 *                inMsgSize     -> The size of the data to be sent.
 *                fPadStxEtx    -> Boolean flag to Pad STX/ETX or not.
 *                inACKTimeOut  -> The timeout within which an ACK should be received.
 *                fWaitForACK   -> Boolean flag to indicate whether to wait for an ACK.
 *                inMaxRetries  -> Maximum number of retries allowed.
 *
 * Output      :  VS_SUCCESS    -> if successful
 *                VS_ERR        -> if error.
 *                ERR_COM_SEND  -> if failed to send the packet.
 *                ERR_COM_NAK   -> Too many NAKs.
 *
 * Notes       :    
 ****************************************************************************
 */
int inRecvCModePacket(int inHandle,char* pchRecBuf,int inMaxRecSize,VS_BOOL fStripStxEtx,int inRespTimeOut,VS_BOOL fSendAck,int inMaxRetries) {
    VS_BOOL fLRC;
    VS_BOOL fEnd = VS_FALSE;
    VS_BOOL fTimedOut = VS_FALSE;
    char chCheckSO = (char) - 1;  /* Default = uninitialized */
    char chAckOrNak;
    char szRecBuf[RECV_BUF_SIZE + 1];
    int inLRC;
    int inNumRetry = 0;
    int inSize;

    do {
        inSize = 0;
        fEnd = VS_FALSE;

        memset(szRecBuf, 0x00, (int)sizeof(szRecBuf));

        inNumRetry++;

        if (inRespTimeOut)    /* If a timeout was passed */
            ulTimerCount = set_itimeout(NO_CLOCK_HANDLE inRespTimeOut, TM_SECONDS);

        while (1)                                      /* Check if Time Expired */ {
            if (inRespTimeOut)    /* If a timeout was passed */ {
                if (CHECK_TIMEOUT(ulTimerCount) == 0) {
                    fTimedOut = VS_TRUE;
                    break;  /* Timeout occurred */
                }
            }

            /* Read one char at a time till we get ETX or SO or EOT */
            if (read(inHandle, szRecBuf + inSize, 1) > 0) {
                inSize++;

                /* Check that we have to exceeded the actual buffer space availalble */
                if (inSize >= sizeof(szRecBuf))
                    return VS_ERROR;

                if (chCheckSO == (char) - 1) {
                    if (szRecBuf[0] == STX)
                        chCheckSO = VS_FALSE;
                    else
                        chCheckSO = VS_TRUE;
                }

                if (fEnd == VS_TRUE)
                    break;

                if ((szRecBuf[inSize - 1] == ETX) && (chCheckSO == VS_FALSE))
                    fEnd = VS_TRUE; /* Break out only after reading LRC */
                else if ((szRecBuf[inSize - 1] == SO) && (chCheckSO == VS_TRUE))
                    fEnd = VS_TRUE;
                else if (szRecBuf[inSize - 1] == EOT)
                    return(VS_ESCAPE);
            }

            if (gu_clr_state())     /* CANCEL key pressed */
                return(VS_ESCAPE);
        } 

        if (inSize == 0 || inSize == 1) {
            // Can't check LRC if only one byte so error out
            fTimedOut = VS_TRUE;
            break;                  /* No response from device - timed out */
        }

        if (szRecBuf[0] == EOT)
            break;                  /* EOT received....break! */

        inLRC = SVC_CRC_CALC(0, &(szRecBuf[1]), inSize - 2);

        /* Should check LRC and set OK */
        fLRC = (char) (szRecBuf[inSize - 1] == inLRC);

        if (szRecBuf[inSize - 1] == 0x00)    /* If LRC == NULL_CH, change so string */
            szRecBuf[inSize - 1] = 0x01;     /* ops will function correctly later */
        szRecBuf[inSize] = 0x00;           /* NULL_CH the packet */

        /* Send ACK or NAK depending on device Response */
        if (fSendAck) {
            chAckOrNak = (char) (fLRC ? ACK : NAK);
            write(inHandle, &chAckOrNak, 1);
        }
    } while ((!fLRC) && (inNumRetry < inMaxRetries));

    if (fTimedOut)
        return (ERR_COM_TIMEOUT);

    /* Strip off STX/ETX if required */
    if (fStripStxEtx) {
        memcpy((char *) szRecBuf, (char *) szRecBuf + 1, (inSize - 3));
        szRecBuf[inSize - 3] = 0;
        inSize -= 3;
    }

    /* Copy to application buffer */
    strncpy(pchRecBuf, szRecBuf, inMaxRecSize);                 

    /* Return smaller of two */
    inSize = (inSize < inMaxRecSize) ? inSize : inMaxRecSize;

    return(inSize);
}

int RS232_CODE_CHECK(char* a,char* b,short* c,char* d,unsigned char* e) {
    GET_VER(a, b, c, d, e);
    return 1;
}

